﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Uml.Activities;
using Microsoft.VisualStudio.Uml.Classes;
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
using System;

namespace HumanWorkflowModelValidation
{
    public class HumanWorkflowValidation
    {
        #region ValidateActivityTypes

        [Export(typeof(System.Action<ValidationContext, object>))]
        [ValidationMethod(ValidationCategories.Save | ValidationCategories.Open | ValidationCategories.Menu)]
        public void ValidateActivityTypes (ValidationContext context, IActivityNode elementToValidate)
        {
            string errorCode = "001";
            string errorMessage = "Error at element: {0} Human workflow only supports Initial Node, Activity Final Node, Decision and Action with stereotype of either ManualStep or AutomaticStep";

            // only validate activity nodes if the owner activity is stereotyped to HumanWorkflow
            if (!IsHumanWorkflowActivity(elementToValidate.Activity))
                return;

            if (! (elementToValidate is IInitialNode ||
                elementToValidate is IFinalNode ||
                elementToValidate is IDecisionNode ||
                ValidateActivityNodeStereotype(elementToValidate)))
            {
                context.LogError(
                    string.Format(errorMessage, elementToValidate.Name),
                    errorCode,
                    new ModelElement[1] { (ModelElement)elementToValidate });            
            }
        }

        private bool ValidateActivityNodeStereotype(IActivityNode activityNode)
        {
            return (activityNode.AppliedStereotypes.Where<IStereotypeInstance>(
                s => s.Name == "ManualStep" || s.Name == "AutomaticStep").Count<IStereotypeInstance>() > 0);           
        }

        private bool IsHumanWorkflowActivity(IActivity activity)
        {
            return (activity.AppliedStereotypes.Where<IStereotypeInstance>(
                s => s.Name == "HumanWorkflow").Count<IStereotypeInstance>() > 0);
        }

        #endregion

        #region ValidateActivityNameUniqueness

        [Export(typeof(System.Action<ValidationContext, object>))]
        [ValidationMethod(ValidationCategories.Save | ValidationCategories.Open | ValidationCategories.Menu)]
        public void ValidateActivityNameUniqueness(ValidationContext context, IActivity elementToValidate)
        {
            string errorCode = "002";
            string errorMessage = "Error at element: {0} elements name is not unique.";
            
            List<string> nodeNames = new List<string>();
            foreach (IActivityNode activityNode in elementToValidate.Nodes)
            {
                if (nodeNames.Contains(activityNode.Name))
                {
                    context.LogError(
                       string.Format(errorMessage, activityNode.Name),
                       errorCode,
                       new ModelElement[1] { (ModelElement)activityNode });      
                }
                else
                {
                    nodeNames.Add(activityNode.Name);
                }            
            }
        }

        #endregion

        #region ValidateConnectorNameRequired

        [Export(typeof(System.Action<ValidationContext, object>))]
        [ValidationMethod(ValidationCategories.Save | ValidationCategories.Open | ValidationCategories.Menu)]
        public void ValidateConnectorGuardRequired(ValidationContext context, IControlFlow connectorToValidate)
        {
            string errorCode = "003";
            string errorMessage = "Error at connector: {0} elements name is required.";

            if (String.IsNullOrEmpty(connectorToValidate.Guard))
            {
                context.LogError(
                          string.Format(errorMessage, connectorToValidate.Name),
                          errorCode,
                           new ModelElement[1] { (ModelElement)connectorToValidate }); 
            }
        }

        #endregion


        // TODO: Manual step esetében a role kötelező

        // TODO: pontosan egy intital és final node
    }
}
